#include "client_handle.h"
#include "ievent.h"
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sstream>
#include <sys/socket.h>

using namespace std;

#define READ_SIZE 65535

ClientHandle::ClientHandle(IEvent* event, int sock)
: mIsOK(true)
, mEvent(event)
, mSock(sock) {
    mEvent->OnConnect(this);
}

ClientHandle::~ClientHandle() {
}

void ClientHandle::Write(const char* buf, int len) {
    string log;
    if (!DoOut(buf, len, log)) {
        Close(log);
    }
}

void ClientHandle::Close(const string& log) {
    bool isok = false;
    {
        unique_lock<mutex> lock(mSendMutex);
        isok = mIsOK.exchange(false);
    }
    if (isok) {
        if (log.size() > 0) {
            mEvent->OnLog(this, log);
        }
        mEvent->OnDisconnect(this);
        shutdown(mSock, 2);
        close(mSock);
    }
}

bool ClientHandle::IsClose() const {
    return !mIsOK.load();
}

int ClientHandle::GetSock() const {
    return mSock;
}

void ClientHandle::OnIn() {
    string log;
    if (!DoIn(log)) {
        Close(log);
    }
}

void ClientHandle::OnOut() {
    string log;
    if (!DoOut(nullptr, 0, log)) {
        Close(log);
    }
}

bool ClientHandle::DoIn(std::string& log) {
    int reval = 0;
    char tmpbuf[READ_SIZE];
    while (1) {
        memset(tmpbuf, 0, sizeof(tmpbuf));
        reval = read(mSock, tmpbuf, sizeof(tmpbuf));
        if (reval < 0) {
            if (errno == EAGAIN) {
                break;
            } else if (errno == EINTR) {
                continue;
            } else {
                log = "Fail to read: " + string(strerror(errno));
                return false;
            }
        } else if (reval == 0) {
            log = "read 0";
            return false;
        } else {
            int pos = mRecvBuf.size();
            mRecvBuf.resize(pos + reval);
            memcpy(&mRecvBuf[pos], tmpbuf, reval);
        }
    }

    if (mRecvBuf.size() > 0) {
        mEvent->OnRead(this, &mRecvBuf[0], mRecvBuf.size());
        mRecvBuf.clear();
    }
    return true;
}

bool ClientHandle::DoOut(const char* buf, int len, std::string& log) {
    unique_lock<mutex> lock(mSendMutex);
    if (!mIsOK.load())
        return true;

    if (buf != nullptr && len > 0) {
        int pos = mSendBuf.size();
        mSendBuf.resize(pos + len);
        memcpy(&mSendBuf[pos], buf, len);
    }

    int total = mSendBuf.size(), size = total, reval = 0;
    while (size) {
        reval = write(mSock, &mSendBuf[total - size], size);
        if (reval < 0) {
            if (errno == EAGAIN) {
                break;
            } else if (errno == EINTR) {
                continue;
            } else {
                log = "Fail to write: " + string(strerror(errno));
                return false;
            }
        } else if (reval == 0) {
            log = "write 0";
            return false;
        } else {
            size -= reval;
        }
    }

    if (size) {
        vector<char> tmp;
        mSendBuf.swap(tmp);
        mSendBuf.resize(size);
        memcpy(&mSendBuf[0], &tmp[total - size], size);
    } else {
        mSendBuf.clear();
    }
    return true;
}
